{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 1. BP神经网络"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1.1 基本原理"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.1.1 前向传播"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"BPNN_Model.png\" style=\"align:center\">"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "第一层为输入层，神经元个数即为数据的特征个数，单下标即表示第几个神经元；\n",
    "\n",
    "第二层为隐藏层，隐藏层可以有多层，隐藏层层数越多，对应得神经网络就越复杂，z 表示隐藏层神经元的输入，a 表示神经元的输出；\n",
    "\n",
    "最后一层为输出层，此层和隐藏层没有太大区别，由于是最后一层，用于产生整个神经网络的输出，故可称为输出层。输入层的神经元并没有对数据进行处理，故输入层的\n",
    "输出即为数据的特征。\n",
    "\n",
    "图中还有参数 w 和 b，其上标表示是神经网络的第几层，w 的双下标表示当前一层和前一层，w 的值即为连接两层之间的权值。由图中线的箭头可以列出下面的式子"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 输入层到隐藏层"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\n",
    "z_1^{(2)}=w_{11}^{(2)}x_1+w_{21}^{(2)}x_2+w_{31}^{(2)}x_3+b_1^{(2)}\\tag{1.1}\n",
    "$$\n",
    "$$\n",
    "z_2^{(2)}=w_{12}^{(2)}x_1+w_{22}^{(2)}x_2+w_{32}^{(2)}x_3+b_2^{(2)}\\tag{1.2}\n",
    "$$\n",
    "$$\n",
    "z_3^{(2)}=w_{13}^{(2)}x_1+w_{23}^{(2)}x_2+w_{33}^{(2)}x_3+b_3^{(2)}\\tag{1.3}\n",
    "$$\n",
    "$$\n",
    "a_1^{(2)}=f(z_1^{(2)})\\tag{1.4}\n",
    "$$\n",
    "$$\n",
    "a_2^{(2)}=f(z_2^{(2)})\\tag{1.5}\n",
    "$$\n",
    "$$\n",
    "a_3^{(2)}=f(z_3^{(2)})\\tag{1.6}\n",
    "$$\n",
    "如果设单个样本是一个行向量$\\boldsymbol{x}=(x_1,\\cdots,x_n)$，那么数据集即为一个$m\\times n$的矩阵，而第一层的权重矩阵应该为一个$n\\times l_2$的矩阵，其中$l_2$表示的是第二层的神经元的个数。所以上式可用矩阵表示为（对单个样本来说）\n",
    "$$\n",
    "\\boldsymbol{z}^{(2)}=\\boldsymbol{xW}^{(2)}\\\\\n",
    "\\boldsymbol{a}^{(2)}=f(\\boldsymbol{z}^{(2)})\n",
    "$$\n",
    "其中上标表示变量所属的层数，而结合矩阵乘积运算，可以得到，最后$\\boldsymbol{z}^{(2)},\\boldsymbol{(a)}^{(2)}$的大小应该是$1\\times l_2$，由此即可继续按此传递下去。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 隐藏层到输出层"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "$$\n",
    "z_1^{(3)}=w_{11}^{(3)}a_1^{(2)}+w_{21}^{(3)}a_2^{(2)}+w_{31}^{(3)}a_3^{(2)}+b_1^{(3)}\\tag{1.7}\n",
    "$$\n",
    "$$\n",
    "z_2^{(3)}=w_{12}^{(3)}a_1^{(2)}+w_{22}^{(3)}a_2^{(2)}+w_{32}^{(3)}a_3^{(2)}+b_2^{(3)}\\tag{1.8}\n",
    "$$\n",
    "$$\n",
    "a_1^{(3)}=f(z_1^{(3)})\\tag{1.10}\n",
    "$$\n",
    "$$\n",
    "a_2^{(3)}=f(z_2^{(3)})\\tag{1.11}\n",
    "$$\n",
    "同理，将上面的式子换成向量运算，可以得到从输入层到隐藏层相同的式子\n",
    "$$\n",
    "\\boldsymbol{z}^{(3)}=\\boldsymbol{a^{(2)}}\\boldsymbol{W}^{(3)}\\\\\n",
    "\\boldsymbol{a}^{(3)}=f(\\boldsymbol{z}^{(3)})\n",
    "$$\n",
    "只不过，此时的样本特征$\\boldsymbol{x}$被替换为了$\\boldsymbol{a}^{(2)}$，而$\\boldsymbol{W}^{(2)}$也应相应替换为$l_2\\times l_3$的$\\boldsymbol{W}^{(3)}$，最终算出的$\\boldsymbol{z}^{(3)},\\boldsymbol{(a)}^{(3)}$的大小由矩阵的乘积运算规律得，应该为$1\\times l_3$。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### 前向传播"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "最后BP 可以归纳出神经网络的前向传播方式为\n",
    "$$\n",
    "\\boldsymbol{x}=\\boldsymbol{a}^{(1)}\\to \\boldsymbol{z}^{(2)}\\to \\cdots \\to \\boldsymbol{a}^{(L-1)}\\to \\boldsymbol{z}^{(L)}\\to \\boldsymbol{a}^{(L)}=\\boldsymbol{y}\n",
    "$$\n",
    "除了输入层外，每层隐藏层和最后的输出层都有一个相应的权重矩阵，和偏置向量，而由上面的推导我们可以总结出，每层的权重矩阵的大小应该为$l_{n-1}\\times l_n,(k=2,\\cdots,L)$。而当$k=2$时，权重矩阵的行数应该与样本特征的数量一致，$l_k$表示的是各层相应的神经元数量，同时，当$k=L$时，那么权重矩阵的列数就和最终希望输出的向量维度一致（一般要分为多少类，就设置为多少个输出神经元，使用独热编码）。此外，偏置矩阵$\\boldsymbol{b}^{(l_k)}$应该是与其相应层的权重矩阵$\\boldsymbol{W}^{(l_k)}$的列向量一样的大小。\n",
    "\n",
    "同时需要注意的是，在进行BP神经网络的参数初始化的时候，就不能够像之前那样，全部赋0或1开始。如果这样做的话，相对于每层的神经元来说，由于其输入其实是一样的，如果所有的参数都一样的话，就会导致某一层的所有神经元的输出也都是一样的，这样做显然是没有意义的。所以对权重和偏置进行赋初值的时候，需要进行随机化赋值。\n",
    "\n",
    "所以前向传播的向量表达式子应该为\n",
    "$$\n",
    "\\boldsymbol{z}^{(l_k)}=\\boldsymbol{a}^{(l_{k-1})}\\boldsymbol{W}^{(l_k)}\\\\\n",
    "\\boldsymbol{a}^{(k)}=f(\\boldsymbol{z}^{(k)})\\tag{1.12}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.1.2 误差反向传播（BP算法）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "信息前向传播指确定参数后，对输入进行处理，即可得到对应输出，但是输\n",
    "出的结果正确与否还要经过调整，这时就要利用误差反向传播来更新参数以逼近期望的输出结果。\n",
    "\n",
    "对于标准 BP 神经网络算法，对一个样本进行训练后即进行参数的更新，其\n",
    "误差函数可写为下式\n",
    "$$\n",
    "E_{(i)}=\\frac{1}{2}\\sum_{k=1}^{l_L}(y_k^{(i)}-o_k^{(i)})^2\\tag{2.1}\n",
    "$$\n",
    "该式即表示每一个训练样本和期望值的误差，总体训练样本的误差函数就可\n",
    "以表示为\n",
    "$$\n",
    "E_{total}=\\frac{1}{N}\\sum_{i=1}^NE_{(i)}\\tag{2.2}\n",
    "$$\n",
    "而误差反向传播的目标就是使得总体误差最小。若用梯度下降法，则参数\n",
    "更新的公式可以表示为:\n",
    "$$\n",
    "\\begin{aligned}\n",
    "W^{(l_k)}\n",
    "&=W^{(l_k)}-\\mu \\frac{\\partial E_{total}}{\\partial W^{(l_k)}}\\\\\n",
    "&=W^{(l_k)}-\\frac{\\mu}{N}\\sum_{i=1}^N\\frac{\\partial E_{(i)}}{\\partial W^{(l_k)}}\n",
    "\\end{aligned}\\tag{2.3}\n",
    "$$\n",
    "$$\n",
    "\\begin{aligned}\n",
    "b^{(l_k)}\n",
    "&=b^{(l_k)}-\\mu \\frac{\\partial E_{total}}{\\partial b^{(l_k)}}\\\\\n",
    "&=b^{(l_k)}-\\frac{\\mu}{N}\\sum_{i=1}^N\\frac{\\partial E_{(i)}}{\\partial b^{(l_k)}}\n",
    "\\end{aligned}\\tag{2.4}\n",
    "$$\n",
    "此时的$l_k(k=2,\\cdots,L)$指的是第$k$层\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "由上式知道，更新参数即需要求得误差函数对其的偏导数。展开单个样本\n",
    "的误差函数 E（以上图中的两层神经网络为例，后面可推广到多层），可得\n",
    "$$\n",
    "E=\\frac{1}{2}(((y_1-f(w_{11}^{(3)}a_1^{(2)}+w_{21}^{(3)}a_2^{(2)}+w_{31}^{(3)}a_3^{(2)}+b_1^{(3)}))^2\\\\\n",
    "+(y_2-f(w_{12}^{(3)}a_1^{(2)}+w_{22}^{(3)}a_2^{(2)}+w_{32}^{(3)}a_3^{(2)}+b_2^{(3)}))^2)\\tag{2.5}\n",
    "$$\n",
    "由求导的链式法则可知:$\\frac{\\partial E}{\\partial w}=\\frac{\\partial E}{\\partial a}\\cdot \\frac{a}{\\partial w}$。故式子可以简化为(由于第二项没有关于$w_{11}^{(3)}$的项，故偏导数为0)\n",
    "$$\n",
    "\\begin{aligned}\n",
    "\\frac{\\partial E}{\\partial w_{11}^{(3)}}\n",
    "&=\\frac{1}{2}\\cdot 2(y_1-a_1^{(3)})(-\\frac{\\partial a_1^{(3)}}{\\partial w_{11}^{(3)}})\\\\\n",
    "&=-(y_1-a_1^{(3)})f'(z_1^{(3)})\\frac{\\partial z_1^{(3)}}{\\partial w_{11}^{(3)}}\\\\\n",
    "&=-(y_1-a_1^{(3)})f'(z_1^{(3)})a_1^{(2)}\n",
    "\\end{aligned}\\tag{2.6}\n",
    "$$\n",
    "上面的推导需要用到：$a_1^{(3)}=f(z_1^{(3)}),z_1^{(3)}=w_{11}^{(3)}a_1^{(2)}+w_{21}^{(3)}a_2^{(2)}+w_{31}^{(3)}a_3^{(2)}+b_1^{(3)}$\n",
    "\n",
    "同时，我们可以先求出其它5个，以便观察规律。\n",
    "$$\n",
    "\\begin{aligned}\n",
    "\\frac{\\partial E}{\\partial w_{21}^{(3)}}&=-(y_1-a_1^{(3)})f'(z_1^{(3)})a_2^{(2)}\\\\\n",
    "\\frac{\\partial E}{\\partial w_{31}^{(3)}}&=-(y_1-a_1^{(3)})f'(z_1^{(3)})a_3^{(2)}\\\\\n",
    "\\frac{\\partial E}{\\partial w_{12}^{(3)}}&=-(y_2-a_2^{(3)})f'(z_2^{(3)})a_1^{(2)}\\\\\n",
    "\\frac{\\partial E}{\\partial w_{22}^{(3)}}&=-(y_2-a_2^{(3)})f'(z_2^{(3)})a_2^{(2)}\\\\\n",
    "\\frac{\\partial E}{\\partial w_{32}^{(3)}}&=-(y_2-a_2^{(3)})f'(z_2^{(3)})a_3^{(2)}\\\\\n",
    "\\end{aligned}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "对隐藏层到输出层的其他相关参数的更新的学习信号也可以同样求得。将\n",
    "求得的隐藏层到输出层的学习信号的值组成的矩阵记为$\\delta^{(l_L)}$，则其向量表示为\n",
    "$$\n",
    "\\delta^{(l_L)}=-(y-a^{(l_L)})\\odot f'(z^{(l_L)})\\tag{2.7}\n",
    "$$\n",
    "其中$\\odot$符号表示矩阵运算中，同型矩阵的对应元素相乘，对于上面来说，$\\delta^{(l_L)}$最后的形状应该和$\\boldsymbol{y}$一致，大小为$1\\times l_L$。\n",
    "\n",
    "输入层到隐藏层和隐藏层之间的学习信号的推导如式子(设对某一层的第$i$个神经元-第几列，的第$j$个权重值，即$w_{ji}^{(l_k)}$求偏导):\n",
    "$$\n",
    "\\begin{aligned}\n",
    "\\frac{\\partial E}{\\partial w_{ji}^{(l_k)}}\n",
    "&=\\frac{\\partial E}{\\partial z_i^{(l_k)}}\\cdot \\frac{\\partial z_i^{(l_k)}}{\\partial w_{ji}^{(l_k)}}\\\\\n",
    "&=\\delta_i^{(l_k)}\\cdot \\frac{\\partial z_i^{(l_k)}}{\\partial w_{ji}^{(l_k)}}\\\\\n",
    "&=\\delta_i^{(l_k)}\\cdot a_j^{(l_{k-1})},(k=2,\\cdots,L)\n",
    "\\end{aligned}\\tag{2.8}\n",
    "$$\n",
    "对所有输出层神经元的权重求偏导有\n",
    "$$\n",
    "\\frac{\\partial E}{\\partial W^{(l_L)}}=a^{(l_{L-1})}\\cdot \\delta^{(l_L)}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "在第$l_k$层的所有神经元中，只有第$i$个神经元才使用到了这个$w^{(l_k)}_{ji}$权重值，即$z^{(l_k)}_i=\\sum^{j}w_{ji}^{(l_k)}a_j^{(l_{k-1})}$，其中\n",
    "$$\n",
    "\\begin{aligned}\n",
    "\\delta_i^{(l_k)}\n",
    "&=\\frac{\\partial E}{\\partial z_i^{(l_k)}}\\\\\n",
    "&=\\sum_{j=1}^{l_{k+1}}\\frac{\\partial E}{\\partial z_j^{(l_{k+1})}}\\cdot \\frac{\\partial z_j^{(l_{k+1})}}{\\partial z_i^{(l_k)}}\\\\\n",
    "&=\\sum_{j=1}^{l_{k+1}}\\delta_j^{(l_{k+1})}\\cdot \\frac{\\partial z_j^{(l_{k+1})}}{\\partial z_i^{(l_k)}}\n",
    "\\end{aligned}\\tag{2.9}\n",
    "$$\n",
    "而上面的式子就是误差反向传播的体现，由于第$l_{k+1}$层的神经元都对$f(z_i^{(l_k)})$做了输入，所以，由此，上面的偏导数就分布到了第$l_{k+1}$层的所有神经元中。\n",
    "根据网络图可以直观地看出第 $l_{k+1}$ 层的输入$𝑧_𝑗^{(l_{k+1})}$对第 $l_k$ 层的输入$z^{(l_k)}_i$的偏导即为$𝑤_{ij}^{(l_{k+1)}}\\cdot 𝑓'(𝑧_𝑖^{(𝑙_k)})$，故第 $l_k$ 层的学习信号可由第 $l_{k+1}$ 层获得。即\n",
    "$$\n",
    "\\begin{aligned}\n",
    "\\delta_i^{(l_k)}\n",
    "&=\\sum_{j=1}^{l_{k+1}}\\delta_j^{(l_{k+1})}w_{ij}^{(l_{k+1})}f'(z_i^{(l_k)})\\\\\n",
    "&=(\\sum_{j=1}^{l_{k+1}}\\delta_j^{(l_{k+1})}w_{ij}^{(l_{k+1})})f'(z_i^{(l_k)})\n",
    "\\end{aligned}\\tag{2.10}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "此式子即为 BP 算法的核心式子，隐藏层的权值和阈值更新即可由此式求\n",
    "得，除此之外，此式子也说明了误差是由后往前传播的。写为向量形式即为\n",
    "$$\n",
    "\\delta^{(l_k)}=((\\delta^{(l_{k+1})}W^{(l_{k+1})})^T)\\odot f'(z^{(l_k)})\\tag{2.11}\n",
    "$$\n",
    "其中$\\delta^{(l_{k+1})}$大小为$1\\times l_{k+1}$，而$W^{(l_{k+1})})^T$大小为$l_{k+1}\\times l_k$，矩阵运算得$((\\delta^{(l_{k+1})}W^{(l_{k+1})})^T)$的大小刚好应该为$1\\times l_k$，由此得到$\\delta^{(l_k)}$的大小也该为$1\\times l_k$。\n",
    "\n",
    "即 BP 神经网络的反向误差传递可以归结为\n",
    "$$\n",
    "\\begin{aligned}\n",
    "\\delta^{(l_L)}&=-(y-a^{(l_L)})\\odot f'(z^{(l_L)})\\\\\n",
    "\\delta^{(l_k)}&=((\\delta^{(l_{k+1})}W^{(l_{k+1})})^T)\\odot f'(z^{(l_k)})\\\\\n",
    "W^{(l_L)}&=W^{(l_L)}-\\mu(a^{(l_{L-1})})^T\\cdot \\delta^{(l_L)}\\\\\n",
    "b^{(l_L)}&=b^{(l_L)}-\\mu \\delta^{(l_L)}\\\\\n",
    "W^{(l_k)}&=W^{(l_k)}-\\mu(a^{(l_{k-1})})^T\\cdot \\delta^{(l_k)}\\\\\n",
    "b^{(l_k)}&=b^{(l_k)}-\\mu \\delta^{(l_k)}\\\\\n",
    "\\end{aligned}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.1.3 激活函数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Sigmoid 函数是一个在生物学中常见的 S 型函数，也称为 S 型生长曲线。在信息科\n",
    "学中，由于其单增以及反函数单增等性质，Sigmoid 函数常被用作神经网络的激活函数，将\n",
    "变量映射到 0,1 之间。函数表达式为\n",
    "$$\n",
    "S(x)=\\frac{1}{1+e^{-x}}\\tag{3.1}\n",
    "$$\n",
    "其导数为\n",
    "$$\n",
    "\\begin{aligned}\n",
    "S'(x)\n",
    "&=\\frac{e^{-\\boldsymbol{x}}}{(1+e^{-\\boldsymbol{x}})^2}\\\\\n",
    "&=\\frac{1}{1+e^{-\\boldsymbol{x}}}\\cdot \\frac{1+e^{-\\boldsymbol{x}}-1}{1+e^{-\\boldsymbol{x}}}\\\\\n",
    "&=\\frac{1}{1+e^{-\\boldsymbol{x}}}\\cdot (1-\\frac{1}{1+e^{-\\boldsymbol{x}}})\\\\\n",
    "&=S(x)\\cdot (1-S(x))\n",
    "\\end{aligned}\\tag{3.2}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 参考"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[BP神经网络算法程序实现对iris的分类](https://www.cnblogs.com/PengLaoShi/p/13778056.html)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 代码"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Sigmoid激活函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x14d7e24aa00>]"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# 支持中文\n",
    "plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签\n",
    "plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号\n",
    "\n",
    "# 激活函数\n",
    "def sigmoid(x):\n",
    "    return 1.0/(1+np.exp(-x))\n",
    "\n",
    "# 导函数\n",
    "#激活函数导函数\n",
    "def sigmoid_derive(x):\n",
    "    # return x*(1-x)\n",
    "    return sigmoid(x)*(1-sigmoid(x))\n",
    "\n",
    "#若x为一个较大的负数,则e^(-x)就会急剧增大，导致溢出\n",
    "\n",
    "#优化的激活函数\n",
    "# def sigmoid(inx):\n",
    "#     if inx>=0:      #对sigmoid函数的优化，避免了出现极大的数据溢出\n",
    "#         return 1.0/(1+exp(-inx))\n",
    "#     else:\n",
    "#         return exp(inx)/(1+exp(inx))\n",
    "\n",
    "\n",
    "# #在numpy数组中，不好直接判断np.array大于零或小于零，所以需要把数组里的数字取出来一个一个判断，计算后，再重新整合\n",
    "# def sigmoid(x):\n",
    "#     x_ravel = x.ravel()  # 将numpy数组展平\n",
    "#     length = len(x_ravel)\n",
    "#     y = []\n",
    "#     for index in range(length):\n",
    "#         if x_ravel[index] >= 0:\n",
    "#             y.append(1.0 / (1 + np.exp(-x_ravel[index])))\n",
    "#         else:\n",
    "#             y.append(np.exp(x_ravel[index]) / (np.exp(x_ravel[index]) + 1))\n",
    "#     return np.array(y).reshape(x.shape)\n",
    "\n",
    "#激活函数可视化\n",
    "xx=np.linspace(-5,5,100)\n",
    "yy=sigmoid(xx)\n",
    "plt.plot(xx,yy)\n",
    "plt.title(r\"激活函数$y=\\frac{1}{1+e^{-x}}$\")\n",
    "\n",
    "\n",
    "# 获取当前的坐标轴, gca = get currekt axis\n",
    "ax = plt.gca()\n",
    "\n",
    "# 设置右边框和上边框，隐藏\n",
    "ax.spines['right'].set_color('none')\n",
    "ax.spines['top'].set_color('none')\n",
    "\n",
    "# 设置x坐标轴为下边框\n",
    "ax.xaxis.set_ticks_position('bottom')\n",
    "# 设置y坐标轴为左边框\n",
    "ax.yaxis.set_ticks_position('left')\n",
    "\n",
    "# 设置x轴, y轴在(0, 0)的位置\n",
    "ax.spines['bottom'].set_position(('data', 0))\n",
    "ax.spines['left'].set_position(('data', 0))\n",
    "\n",
    "# 渐近线\n",
    "top=np.ones_like(xx)\n",
    "bottom=np.zeros_like(xx)\n",
    "plt.plot(xx,top,linestyle='--')\n",
    "plt.plot(xx,bottom,linestyle='--')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## tanh激活函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导函数\n",
    "def tanh(x):\n",
    "    return np.tanh(x)\n",
    "\n",
    "#导函数\n",
    "def tanh_deriv(x):\n",
    "    return 1.0 - np.tanh(x) * np.tanh(x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 自定义函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "#列表展示函数\n",
    "def shape_list(ndarray_list,content=False):\n",
    "    print('-'*100)\n",
    "    for i in ndarray_list:\n",
    "        print(np.shape(i),'\\t',end='')\n",
    "        if content:\n",
    "            print(f'{i}:')\n",
    "    print('\\n'+'-'*100)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1.2 读入数据"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "初识数据集，特征矩阵：(1797, 64)\t 标签：(1797,)\n",
      "数据集和标签的类型为： <class 'numpy.ndarray'>\n",
      "数据集的样本量和特征数分别为： (1797, 64)\n",
      "转换为独热编码后整个数据集的的标签矩阵大小为： (1797, 10)\n",
      "原始标签2\t 独热编码[0 0 1 0 0 0 0 0 0 0]\n",
      "原始标签2\t 独热编码[0 0 1 0 0 0 0 0 0 0]\n",
      "原始标签1\t 独热编码[0 1 0 0 0 0 0 0 0 0]\n",
      "原始标签5\t 独热编码[0 0 0 0 0 1 0 0 0 0]\n",
      "原始标签3\t 独热编码[0 0 0 1 0 0 0 0 0 0]\n",
      "原始标签4\t 独热编码[0 0 0 0 1 0 0 0 0 0]\n",
      "原始标签4\t 独热编码[0 0 0 0 1 0 0 0 0 0]\n",
      "原始标签9\t 独热编码[0 0 0 0 0 0 0 0 0 1]\n",
      "原始标签2\t 独热编码[0 0 1 0 0 0 0 0 0 0]\n",
      "原始标签0\t 独热编码[1 0 0 0 0 0 0 0 0 0]\n"
     ]
    }
   ],
   "source": [
    "from sklearn.datasets import load_digits    #导入数据集\n",
    "from sklearn.datasets import load_iris\n",
    "from sklearn.preprocessing import LabelBinarizer     #独热编码\n",
    "import random\n",
    "\n",
    "#手写数据集\n",
    "X,y=load_digits(return_X_y=True)\n",
    "# X,y=load_iris(return_X_y=True)\n",
    "print(\"初识数据集，特征矩阵：{}\\t 标签：{}\".format(np.shape(X),np.shape(y)))      #形状\n",
    "print(f\"数据集和标签的类型为：\",type(X))\n",
    "\n",
    "m,n=np.shape(X)             #记录样本量m和特征数n\n",
    "print(f\"数据集的样本量和特征数分别为：\",np.shape(X))\n",
    "\n",
    "#将样本标签转化为独热编码\n",
    "Y= LabelBinarizer().fit_transform(y)\n",
    "print(f\"转换为独热编码后整个数据集的的标签矩阵大小为：\",np.shape(Y))\n",
    "#独热编码展示\n",
    "for i in [random.randint(0,m-1) for x in range(10) ]:\n",
    "    print(f\"原始标签{y[i]}\\t 独热编码{Y[i,:]}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.2.1 数据集划分"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "训练集：X_train=(1257, 64)； Y_train=(1257, 10)\n",
      "测试集：X_test=(540, 64)； Y_test=(540, 10)\n"
     ]
    }
   ],
   "source": [
    "from sklearn.model_selection import train_test_split        #训练集/测试集划分\n",
    "X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.3, random_state=42)       #测试集占30%，其余用作训练集\n",
    "print(f\"训练集：X_train={np.shape(X_train)}； Y_train={np.shape(y_train)}\")\n",
    "print(f\"测试集：X_test={np.shape(X_test)}； Y_test={np.shape(y_test)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1.3 构建模型"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "\n",
    "\n",
    "#定义BP神经网络类\n",
    "class BPNeuralNetwork():\n",
    "\n",
    "    #初始化，传入各层隐藏层神经元个数（列表形式）\n",
    "    def __init__(self,hidden_layer_list,activate_choose=\"sigmoid\") -> None:\n",
    "        self.hidden_layer_list=hidden_layer_list        #各隐藏层神经元数量列表\n",
    "        self.w_list=[]\n",
    "        self.b_list=[]\n",
    "        self.mse_list=[]          #误差记录\n",
    "        self.activate_choose=activate_choose\n",
    "        self._choose_activation()\n",
    "        self._info()\n",
    "        \n",
    "    # 选择激活函数\n",
    "    def _choose_activation(self):\n",
    "        # 判断使用的激活函数\n",
    "        if self.activate_choose==\"sigmoid\":\n",
    "            self.activate_function=sigmoid\n",
    "            self.activate_function_derive=sigmoid_derive\n",
    "        elif self.activate_choose==\"tanh\":\n",
    "            self.activate_function=tanh\n",
    "            self.activate_function_derive=tanh_deriv\n",
    "\n",
    "    def _info(self):\n",
    "        print('-'*100)\n",
    "        print('模型信息：')\n",
    "        for i,num in enumerate(self.hidden_layer_list):\n",
    "            print(f'\\t第{i+1}层隐藏层有{num}个神经元')\n",
    "\n",
    "        print(f'\\t激活函数使用{self.activate_choose}')\n",
    "\n",
    "    #训练函数\n",
    "    def fit(self,X,y,lr=0.01,iter_num=500):\n",
    "        '''\n",
    "        :param X: 数据集，形式为mxn，m为样本量，n为特征数\n",
    "        :param y: 标签，要是独热编码，即mxc,c为类别数\n",
    "        '''\n",
    "        print(\"开始训练：\")\n",
    "        #记录数据集大小，样本量m，特征数n\n",
    "        m,n=np.shape(X)\n",
    "        #统计分类数c\n",
    "        c=np.shape(y)[-1]\n",
    "        #构建网络-主要是隐藏层，不包含输出层\n",
    "        for i,num in enumerate(self.hidden_layer_list):\n",
    "            print(f\"第{i+1}层隐藏层：{num}个神经元。\")\n",
    "\n",
    "            if i==0:\n",
    "                #第一层隐藏层\n",
    "                # print(f\"第一层隐藏层\")\n",
    "                #权重\n",
    "                w=np.random.randn(n,num)\n",
    "            else:\n",
    "                #其它层\n",
    "                #权重\n",
    "                w=np.random.randn(self.hidden_layer_list[i-1],num)\n",
    "            #偏置是统一的\n",
    "            b=np.random.randn(1,num)\n",
    "\n",
    "            self.w_list.append(w)\n",
    "            self.b_list.append(b)\n",
    "\n",
    "        #输出层\n",
    "        #权重\n",
    "        w=np.random.randn(self.hidden_layer_list[-1],c)\n",
    "        #偏置\n",
    "        b=np.random.randn(1,c)\n",
    "\n",
    "        self.w_list.append(w)\n",
    "        self.b_list.append(b)\n",
    "\n",
    "        # print(f\"网络构建完毕\")\n",
    "        # shape_list(self.w_list)\n",
    "        # shape_list(self.b_list)\n",
    "\n",
    "        #迭代次数计数\n",
    "        count=1\n",
    "\n",
    "        #开始每轮迭代\n",
    "        while count<=iter_num:\n",
    "            #开始训练——标准梯度下降法\n",
    "            for sample,label in zip(X,y):       #取每个样本和标签，此时标签为独热编码\n",
    "                \n",
    "                sample=np.atleast_2d(sample)   \n",
    "\n",
    "                #各层输出输出列表\n",
    "                z_list=[sample]     #输入层输入\n",
    "                a_list=[sample]     #输入层输出\n",
    "\n",
    "                #计算其它各层输入和输出\n",
    "                for i in range(len(self.w_list)):\n",
    "                    z=a_list[i].dot(self.w_list[i])\n",
    "                    a=self.activate_function(z)\n",
    "\n",
    "                    z_list.append(z)            #记录各层输入\n",
    "                    a_list.append(a)            #记录各层输出\n",
    "\n",
    "\n",
    "                #输出层学习信号\n",
    "                lso=-(label-a_list[-1])*self.activate_function_derive(z_list[-1])\n",
    "\n",
    "                #学习信号列表\n",
    "                ls_list=[lso]\n",
    "\n",
    "                #推导各层学习信号\n",
    "                for i in range(1,len(self.w_list)):\n",
    "                    ls=ls_list[-1].dot(self.w_list[-i].T)*self.activate_function_derive(z_list[-i-1])\n",
    "\n",
    "                    ls_list.append(ls)          #记录各层学习信号\n",
    "\n",
    "                #更新参数\n",
    "                for i in range(len(self.w_list)):\n",
    "                    self.w_list[i]-=lr*(a_list[i].T).dot(ls_list[-i-1])\n",
    "                    self.b_list[i]-=lr*ls_list[-i-1]\n",
    "                \n",
    "\n",
    "            #每迭代一次计算总体平均误差\n",
    "            y_p=self.predict(X)     #预测值\n",
    "            # print(y_p)\n",
    "            mse=np.mean((y-y_p)**2)\n",
    "            self.mse_list.append(mse)\n",
    "\n",
    "            if count%50==0:\n",
    "                print(f\"第{count}次迭代结束\")\n",
    "\n",
    "                print(f\"总体均方差为：{mse}\")\n",
    "\n",
    "            count+=1\n",
    "\n",
    "    #预测函数\n",
    "    def predict(self,X):\n",
    "        \n",
    "        a=X\n",
    "        for w,b in zip(self.w_list,self.b_list):\n",
    "            a=self.activate_function(a.dot(w)+b)\n",
    "\n",
    "        return a\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.3.1 训练"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "----------------------------------------------------------------------------------------------------\n",
      "模型信息：\n",
      "\t第1层隐藏层有32个神经元\n",
      "\t激活函数使用sigmoid\n",
      "开始训练：\n",
      "第1层隐藏层：32个神经元。\n",
      "第50次迭代结束\n",
      "总体均方差为：0.04801573865258159\n",
      "第100次迭代结束\n",
      "总体均方差为：0.038581339808163286\n",
      "第150次迭代结束\n",
      "总体均方差为：0.02818502930456438\n",
      "第200次迭代结束\n",
      "总体均方差为：0.024591272313319267\n",
      "第250次迭代结束\n",
      "总体均方差为：0.021717668279090965\n",
      "第300次迭代结束\n",
      "总体均方差为：0.018055841707006963\n",
      "第350次迭代结束\n",
      "总体均方差为：0.014407014328466257\n",
      "第400次迭代结束\n",
      "总体均方差为：0.012652941298123511\n",
      "第450次迭代结束\n",
      "总体均方差为：0.011897368668963494\n",
      "第500次迭代结束\n",
      "总体均方差为：0.01091234016687908\n"
     ]
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "# 支持中文\n",
    "plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签\n",
    "plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号\n",
    "\n",
    "lr=0.01         #学习率\n",
    "iter_num=500        #迭代次数\n",
    "hidden_layer_list=[32]          #各隐藏层神经元个数列表，经测试多层也能正常运行\n",
    "\n",
    "clf=BPNeuralNetwork(hidden_layer_list,activate_choose=\"sigmoid\")      #构建分类器\n",
    "\n",
    "clf.fit(X_train,y_train,lr=lr,iter_num=iter_num)        #开始训练\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x14d7f859700>]"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 864x576 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "from sklearn.metrics import accuracy_score,confusion_matrix\n",
    "\n",
    "y_p=clf.predict(X_train)                 #预测\n",
    "y_pp=np.argmax(y_p,axis=1)              #将预测出来的值转为相应标签\n",
    "y_tt=np.argmax(y_train,axis=1)           #独热编码逆转换为标签\n",
    "# print(np.argmax(y_pp,axis=1))\n",
    "# print(np.argmax(y_test,axis=1))\n",
    "\n",
    "acc_train=accuracy_score(y_pp,y_tt)\n",
    "# print(f\"训练集精度：{acc_train}\")\n",
    "\n",
    "#可视化\n",
    "mse_list=clf.mse_list       #获取均方差\n",
    "\n",
    "iter_num_list=[i for i in range(1,len(mse_list)+1)]\n",
    "\n",
    "plt.figure(figsize=(12,8))\n",
    "plt.title(f\"训练集精度：{acc_train}\")\n",
    "plt.xlabel(\"iter_num(迭代次数)\")\n",
    "plt.ylabel(\"mse总体均方差\")\n",
    "plt.plot(iter_num_list,mse_list)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从上图可以看出，总体的mse的趋势是下降的，证明分类器的训练是往预期的方向发展。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1.3.2 测试"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "#绘制混淆矩阵函数\n",
    "def plot_confusion_matrix(y_true, y_pred, classes,title=\"混淆矩阵\",\n",
    "                          cmap=plt.cm.Blues, save_flg=False):\n",
    "    ''' \n",
    "    :param y_true:真实值\n",
    "    '''\n",
    "\n",
    "    labels = range(c)           #迭代器      \n",
    "    #混淆矩阵\n",
    "    cm = confusion_matrix(y_tt, y_pp, labels=labels)\n",
    "\n",
    "    #画布\n",
    "    plt.figure(figsize=(8, 8))\n",
    "    plt.imshow(cm, interpolation='nearest', cmap=cmap)\n",
    "    plt.title(title, fontsize=7.5,fontfamily=\"SimSun\")\n",
    "\n",
    "    plt.colorbar()  #颜色条\n",
    "    \n",
    "    tick_marks = np.arange(len(classes))\n",
    "    #横坐标刻度\n",
    "    plt.xticks(tick_marks, classes, fontsize=7.5)\n",
    "    #纵坐标刻度\n",
    "    plt.yticks(tick_marks, classes, fontsize=7.5)\n",
    "    \n",
    "    #阈值\n",
    "    thresh = cm.max() / 2.\n",
    "    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):\n",
    "        #在相应格子上写上值\n",
    "        plt.text(j, i, cm[i, j],\n",
    "                 horizontalalignment=\"center\",\n",
    "                 color=\"white\" if cm[i, j] > thresh else \"black\")\n",
    "    #纵坐标\n",
    "    plt.ylabel('真实值', fontsize=7.5,fontfamily=\"SimSun\")\n",
    "    #横坐标\n",
    "    plt.xlabel('预测值', fontsize=7.5,fontfamily=\"SimSun\")\n",
    "    #是否保存\n",
    "    if save_flg:\n",
    "        plt.savefig(\"./confusion_matrix.png\")\n",
    "    plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "测试集精度：0.9055555555555556\n"
     ]
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 576x576 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "import itertools\n",
    "\n",
    "y_p=clf.predict(X_test)                 #预测\n",
    "y_pp=np.argmax(y_p,axis=1)              #将预测出来的值转为相应标签\n",
    "y_tt=np.argmax(y_test,axis=1)           #独热编码逆转换为标签\n",
    "# print(np.argmax(y_pp,axis=1))\n",
    "# print(np.argmax(y_test,axis=1))\n",
    "\n",
    "acc_test=accuracy_score(y_pp,y_tt)\n",
    "print(f\"测试集精度：{acc_test}\")\n",
    "\n",
    "c=np.shape(y_test)[-1]              #获取分类数\n",
    "classes = [str(i) for i in range(c)]    #类别标签\n",
    "\n",
    "#绘制混淆矩阵\n",
    "plot_confusion_matrix(y_true=y_tt,y_pred=y_pp,classes=classes,title=\"测试集-混淆矩阵\")\n",
    "\n"
   ]
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "7d4f0b51f0efcdca2d59772b797edc66142e696821d7081ab28b8370b1f74ff6"
  },
  "kernelspec": {
   "display_name": "Python 3.9.7 64-bit ('venv': venv)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.8"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
